#!/usr/bin/env python
'''
Parse a MAVLink protocol XML file and generate a Java implementation

Copyright Andrew Tridgell 2011
Released under GNU GPL version 3 or later
'''
from __future__ import print_function

from builtins import range
from builtins import object

import os
from . import mavparse, mavtemplate

t = mavtemplate.MAVTemplate()

def generate_enums(basename, xml):
    '''generate main header per XML file'''
    directory = os.path.join(basename, '''enums''')
    mavparse.mkdir_p(directory)
    for en in xml.enum:
        f = open(os.path.join(directory, en.name+".java"), mode='w')
        t.write(f, '''
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * java mavlink generator tool. It should not be modified by hand.
 */

package com.MAVLink.enums;

/**
 * ${description}
 */
public class ${name} {
''', en)

        for entry in en.entry:
            if entry.value > 2147483647:
                t.write(f, '''
   public static final long ${name} = ${value}L; /* ${description} |${{param:${description}| }} */
''', entry)
            else:
                t.write(f, '''
   public static final int ${name} = ${value}; /* ${description} |${{param:${description}| }} */
''', entry)

        t.write(f, '''
}''')
        f.close()



def generate_CRC(directory, xml):
    '''generate CRC definition and crc array per dialect'''
    xml.message_crcs_array = ''
    for msgid, crc in sorted(xml.message_crcs.items()):
        xml.message_crcs_array += 'MAVLINK_MESSAGE_CRCS.put(%u, %u);\n        ' % (msgid, crc)

    f = open(os.path.join(directory, "CRC.java"), mode='w')
    t.write(f,'''
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * java mavlink generator tool. It should not be modified by hand.
 */

package com.MAVLink.${basename};

import java.util.HashMap;
import java.util.Map;

/**
 * CRC-16/MCRF4XX calculation for MAVlink messages. The checksum must be
 * initialized, updated with which field of the message, and then finished with
 * the message id.
 *
 */
public class CRC {
    private static final Map<Integer, Integer> MAVLINK_MESSAGE_CRCS;
    private static final int CRC_INIT_VALUE = 0xffff;
    private int crcValue;

    static {
        MAVLINK_MESSAGE_CRCS = new HashMap<>();
        ${message_crcs_array}
    }

    /**
     * Accumulate the CRC by adding one char at a time.
     *
     * The checksum function adds the hash of one char at a time to the 16 bit
     * checksum (uint16_t).
     *
     * @param data new char to hash
     **/
    public void update_checksum(int data) {
        data = data & 0xff; //cast because we want an unsigned type
        int tmp = data ^ (crcValue & 0xff);
        tmp ^= (tmp << 4) & 0xff;
        crcValue = ((crcValue >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xf);
    }

    /**
     * Finish the CRC calculation of a message, by running the CRC with the
     * Magic Byte.
     *
     * @param msgid The message id number
     * @return boolean True if the checksum was successfully finished. Otherwise false
     */
    public boolean finish_checksum(int msgid) {
        if (MAVLINK_MESSAGE_CRCS.containsKey(msgid)) {
            update_checksum(MAVLINK_MESSAGE_CRCS.get(msgid));
            return true;
        }
        return false;
    }

    /**
     * Initialize the buffer for the CRC16/MCRF4XX
     */
    public void start_checksum() {
        crcValue = CRC_INIT_VALUE;
    }

    public int getMSB() {
        return ((crcValue >> 8) & 0xff);
    }

    public int getLSB() {
        return (crcValue & 0xff);
    }

    public CRC() {
        start_checksum();
    }

}
        ''',xml)

    f.close()


def generate_message_h(directory, m):
    '''generate per-message header for a XML file'''
    f = open(os.path.join(directory, 'msg_%s.java' % m.name_lower), mode='w')

    (path_head, path_tail) = os.path.split(directory)
    if path_tail == "":
        (path_head, path_tail) = os.path.split(path_head)
    t.write(f, '''
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * java mavlink generator tool. It should not be modified by hand.
 */

// MESSAGE ${name} PACKING
package com.MAVLink.%s;
import com.MAVLink.MAVLinkPacket;
import com.MAVLink.Messages.MAVLinkMessage;
import com.MAVLink.Messages.MAVLinkPayload;
import com.MAVLink.Messages.Units;
import com.MAVLink.Messages.Description;

/**
 * ${description}
 */
public class msg_${name_lower} extends MAVLinkMessage {

    public static final int MAVLINK_MSG_ID_${name} = ${id};
    public static final int MAVLINK_MSG_LENGTH = ${wire_length};
    private static final long serialVersionUID = MAVLINK_MSG_ID_${name};

    ${{ordered_fields:
    /**
     * ${description}
     */
    @Description("${description}")
    @Units("${units}")
    public ${type} ${name}${array_suffix};
    }}

    /**
     * Generates the payload for a mavlink message for a message of this type
     * @return
     */
    @Override
    public MAVLinkPacket pack() {
        MAVLinkPacket packet = new MAVLinkPacket(MAVLINK_MSG_LENGTH,isMavlink2);
        packet.sysid = sysid;
        packet.compid = compid;
        packet.msgid = MAVLINK_MSG_ID_${name};

        ${{base_fields:${packField}
        }}
        if (isMavlink2) {
            ${{extended_fields: ${packField}
            }}
        }
        return packet;
    }

    /**
     * Decode a ${name_lower} message into this class fields
     *
     * @param payload The message to decode
     */
    @Override
    public void unpack(MAVLinkPayload payload) {
        payload.resetIndex();

        ${{base_fields:${unpackField}
        }}
        if (isMavlink2) {
            ${{extended_fields: ${unpackField}
            }}
        }
    }

    /**
     * Constructor for a new message, just initializes the msgid
     */
    public msg_${name_lower}() {
        this.msgid = MAVLINK_MSG_ID_${name};
    }

    /**
     * Constructor for a new message, initializes msgid and all payload variables
     */
    public msg_${name_lower}(${{ordered_fields: ${type}${array_suffix_empty} ${name},}}) {
        this.msgid = MAVLINK_MSG_ID_${name};

        ${{ordered_fields:this.${name} = ${name};
        }}
    }

    /**
     * Constructor for a new message, initializes everything
     */
    public msg_${name_lower}(${{ordered_fields: ${type}${array_suffix_empty} ${name},}}, int sysid, int compid, boolean isMavlink2) {
        this.msgid = MAVLINK_MSG_ID_${name};
        this.sysid = sysid;
        this.compid = compid;
        this.isMavlink2 = isMavlink2;

        ${{ordered_fields:this.${name} = ${name};
        }}
    }

    /**
     * Constructor for a new message, initializes the message with the payload
     * from a mavlink packet
     *
     */
    public msg_${name_lower}(MAVLinkPacket mavLinkPacket) {
        this.msgid = MAVLINK_MSG_ID_${name};

        this.sysid = mavLinkPacket.sysid;
        this.compid = mavLinkPacket.compid;
        this.isMavlink2 = mavLinkPacket.isMavlink2;
        unpack(mavLinkPacket.payload);
    }

    ${{ordered_fields: ${getText} }}
    /**
     * Returns a string with the MSG name and data
     */
    @Override
    public String toString() {
        return "MAVLINK_MSG_ID_${name} - sysid:"+sysid+" compid:"+compid+${{ordered_fields:" ${name}:"+${name}+}}"";
    }

    /**
     * Returns a human-readable string of the name of the message
     */
    @Override
    public String name() {
        return "MAVLINK_MSG_ID_${name}";
    }
}
        ''' % path_tail, m)
    f.close()


def generate_MAVLinkMessage(directory, xml_list):
    f = open(os.path.join(directory, "MAVLinkPacket.java"), mode='w')

    imports = []

    for xml in xml_list:
        importString = "import com.MAVLink.{}.*;".format(xml.basename)
        imports.append(importString)

    xml_list[0].importString = os.linesep.join(imports)

    t.write(f, '''
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * java mavlink generator tool. It should not be modified by hand.
 */

package com.MAVLink;

import java.io.Serializable;
import com.MAVLink.Messages.MAVLinkPayload;
import com.MAVLink.Messages.MAVLinkMessage;
import com.MAVLink.${basename}.CRC;

${importString}

/**
 * Common interface for all MAVLink Messages
 * Packet Anatomy
 * This is the anatomy of one packet. It is inspired by the CAN and SAE AS-4 standards.
 *
 * MAVLink 1 Packet Format
 *
 * Byte Index  Content              Value       Explanation
 * 0            Packet start sign  v1.0: 0xFE   Indicates the start of a new packet.  (v0.9: 0x55; v1.0: 0xFE; v2.0 0xFD)
 * 1            Payload length      0 - 255     Indicates length of the following payload.
 * 2            Packet sequence     0 - 255     Each component counts up its send sequence. Allows to detect packet loss
 * 3            System ID           1 - 255     ID of the SENDING system. Allows to differentiate different MAVs on the same network.
 * 4            Component ID        0 - 255     ID of the SENDING component. Allows to differentiate different components of the same system, e.g. the IMU and the autopilot.
 * 5            Message ID          0 - 255     ID of the message - the id defines what the payload means and how it should be correctly decoded.
 * 6 to (n+6)   Payload             0 - 255     Data of the message, depends on the message id.
 * (n+7)to(n+8) Checksum (low byte, high byte)  CRC16/MCRF4XX hash, excluding packet start sign, so bytes 1..(n+6) Note: The checksum also includes MAVLINK_CRC_EXTRA (Number computed from message fields. Protects the packet from decoding a different version of the same packet but with different variables).
 *
 * The checksum is the CRC16/MCRF4XX. Please see the MAVLink source code for a documented C-implementation of it. LINK TO CHECKSUM
 * The minimum packet length is 8 bytes for acknowledgement packets without payload
 * The maximum packet length is 263 bytes for full payload
 *
 *
 * MAVLink 2 Packet Format
 *
 * Byte Index     Content             Value              Explanation
 * 0              Packet start sign  v2.0: 0xFD          Indicates the start of a new packet.  (v0.9: 0x55; v1.0: 0xFE; v2.0 0xFD)
 * 1              Payload length      0 - 255            Indicates length of the following payload.
 * 2              Incompatible Flags  0 - 255            Flags that must be understood
 * 3              Compatible Flags    0 - 255            Flags that can be ignored if not understood
 * 4              Packet sequence     0 - 255            Each component counts up its send sequence. Allows to detect packet loss
 * 5              System ID           1 - 255            ID of the SENDING system. Allows to differentiate different MAVs on the same network.
 * 6              Component ID        0 - 255            ID of the SENDING component. Allows to differentiate different components of the same system, e.g. the IMU and the autopilot.
 * 7 to 9         Message ID          0 - 16777216       ID of the message - the id defines what the payload means and how it should be correctly decoded.
 * 10             Target System ID    1 - 255            (OPTIONAL) ID of the TARGET system. Only used for point-to-point mode
 * 11             Target Component ID 0 - 255            (OPTIONAL) ID of the TARGET component. Only used for point-to-point mode
 * 12 to (n+12)   Payload             0 - 255            Data of the message, depends on the message id.
 * (n+13)to(n+14) Checksum (low byte, high byte)         CRC16/MCRF4XX hash, excluding packet start sign, so bytes 1..(n+6) Note: The checksum also includes MAVLINK_CRC_EXTRA (Number computed from message fields. Protects the packet from decoding a different version of the same packet but with different variables).
 * (n+15)to(n+27) Signature (typeid, timestamp, sha256)  (OPTIONAL) Signature which allows ensuring that the link is tamper-proof; 13 bytes containing typeid (1 byte), timestamp (6 bytes), and last 6 bytes of SHA256 hash
 *
 * The signature is a combination of a typeid, timestamp, and SHA256 hash.
 * OPTIONAL fields mean that, if they are not used, they do not exist in the MAVLink frame at all. Typically target sysid and target compid are not used, and signature is only used if signing is set up between both ends.
 *
 * @see <a href="https://mavlink.io">mavlink.io</a> for more documentation on the MAVLink protocol
 */
public class MAVLinkPacket implements Serializable {
    private static final long serialVersionUID = 2095947771227815314L;

    public static final int MAVLINK_STX_MAVLINK1 = 0xFE; // 254
    public static final int MAVLINK_STX_MAVLINK2 = 0xFD; // 253
    public static final int MAVLINK1_HEADER_LEN = 6;
    public static final int MAVLINK2_HEADER_LEN = 10;
    public static final int MAVLINK1_NONPAYLOAD_LEN = MAVLINK1_HEADER_LEN + 2;
    public static final int MAVLINK2_NONPAYLOAD_LEN = MAVLINK2_HEADER_LEN + 2;

    static final boolean V = false;
    static void logv(String str) {
        if(V) System.out.println(String.format("MAVLinkPacket: %s", str));
    }

    /**
     * Payload length
     */
    public final int len;

    /**
     * Message sequence
     */
    public int seq;

    /**
     * ID of the SENDING system. Allows to differentiate different MAVs on the
     * same network.
     */
    public int sysid;

    /**
     * ID of the SENDING component. Allows to differentiate different components
     * of the same system, e.g. the IMU and the autopilot.
     */
    public int compid;

    /**
     * ID of the message - the id defines what the payload means and how it
     * should be correctly decoded.
     */
    public int msgid;

    /**
     * Data of the message, depends on the message id.
     */
    public MAVLinkPayload payload;

    /**
    * CRC-16/MCRF4XX hash, excluding packet start sign, so bytes 1..(n+HEADER-LENGTH)
    * Note: The checksum also includes MAVLINK_CRC_EXTRA (Number computed from
    * message fields. Protects the packet from decoding a different version of
    * the same packet but with different variables).
    */
    public CRC crc;

    // MAVLink 2.0 fields

    /**
     * Flag to indicate which MAVLink version this packet is
     */
    public boolean isMavlink2;

    /**
     * Flags that must be understood
     */
    public int incompatFlags;

    /**
     * Flags that can be ignored if not understood
     */
    public int compatFlags;

    public MAVLinkPacket(int payloadLength) {
        this(payloadLength, false);
    }

    public MAVLinkPacket(final int payloadLength, final boolean isMavlink2) {
        len = payloadLength;
        payload = new MAVLinkPayload();
        this.isMavlink2 = isMavlink2;
    }

    /**
     * Check if the size of the Payload is equal to the "len" byte
     */
    public boolean payloadIsFilled() {
        return payload.size() >= len;
    }

    /**
     * Update CRC for this packet.
     * @return boolean True if the CRC was successfully updated. Otherwise false
     */
    public boolean generateCRC(final int payloadSize) {
        if (crc == null) {
            crc = new CRC();
        } else {
            crc.start_checksum();
        }

        if (isMavlink2) {
            crc.update_checksum(payloadSize);
            crc.update_checksum(incompatFlags);
            crc.update_checksum(compatFlags);
            crc.update_checksum(seq);
            crc.update_checksum(sysid);
            crc.update_checksum(compid);
            crc.update_checksum(msgid);
            crc.update_checksum(msgid >>> 8);
            crc.update_checksum(msgid >>> 16);
        } else {
            crc.update_checksum(payloadSize);
            crc.update_checksum(seq);
            crc.update_checksum(sysid);
            crc.update_checksum(compid);
            crc.update_checksum(msgid);
        }

        payload.resetIndex();

        for (int i = 0; i < payloadSize; i++) {
            crc.update_checksum(payload.getByte());
        }
        return crc.finish_checksum(msgid);
    }

    /**
     * Return length of actual data after trimming zeros at the end.
     * @param payload
     * @return minimum length of valid data
     */
    private int mavTrimPayload(final byte[] payload)
    {
        int length = payload.length;
        while (length > 1 && payload[length-1] == 0) {
            length--;
        }
        return length;
    }

    /**
     * Encode this packet for transmission.
     *
     * @return Array with bytes to be transmitted
     */
    public byte[] encodePacket() {
        final int bufLen;
        final int payloadSize;

        if (isMavlink2) {
            payloadSize = mavTrimPayload(payload.payload.array());
            bufLen = MAVLINK2_HEADER_LEN + payloadSize + 2;
        } else {
            payloadSize = payload.size();
            bufLen = MAVLINK1_HEADER_LEN + payloadSize + 2;

        }
        byte[] buffer = new byte[bufLen];

        int i = 0;
        if (isMavlink2) {
            buffer[i++] = (byte) MAVLINK_STX_MAVLINK2;
            buffer[i++] = (byte) payloadSize;
            buffer[i++] = (byte) incompatFlags;
            buffer[i++] = (byte) compatFlags;
            buffer[i++] = (byte) seq;
            buffer[i++] = (byte) sysid;
            buffer[i++] = (byte) compid;
            buffer[i++] = (byte) (msgid & 0XFF);
            buffer[i++] = (byte) ((msgid >>> 8) & 0XFF);
            buffer[i++] = (byte) ((msgid >>> 16) & 0XFF);
        } else {
            buffer[i++] = (byte) MAVLINK_STX_MAVLINK1;
            buffer[i++] = (byte) payloadSize;
            buffer[i++] = (byte) seq;
            buffer[i++] = (byte) sysid;
            buffer[i++] = (byte) compid;
            buffer[i++] = (byte) msgid;
        }

        for (int j = 0; j < payloadSize; ++j) {
            buffer[i++] = payload.payload.get(j);
        }

        generateCRC(payloadSize);
        buffer[i++] = (byte) (crc.getLSB());
        buffer[i++] = (byte) (crc.getMSB());

        logv(String.format("encode: isMavlink2=%s msgid=%d", isMavlink2, msgid));

        return buffer;
    }
        ''', xml_list[0])

    f.write('''
    /**
     * Unpack the data in this packet and return a MAVLink message
     *
     * @return MAVLink message decoded from this packet
     */
    public MAVLinkMessage unpack() {
        switch (msgid) {
        ''')

    # sort msgs by id
    xml_msgs = []
    for xml in xml_list:
        for msg in xml.message:
            xml_msgs.append(msg)
    xml_msgs.sort(key=lambda msg: msg.id)

    for msg in xml_msgs:
        t.write(f, '''
            case msg_${name_lower}.MAVLINK_MSG_ID_${name}:
                return  new msg_${name_lower}(this);
            ''',msg)
    f.write('''
            default:
                return null;
        }
    }
''')

    f.write('''
}
''')
    f.close()

def copy_fixed_headers(directory, xml):
    '''copy the fixed protocol headers to the target directory'''
    import shutil
    hlist = [ 'Parser.java', 'Messages/MAVLinkMessage.java', 'Messages/MAVLinkPayload.java', 'Messages/MAVLinkStats.java',
              'Messages/Description.java', 'Messages/Units.java', 'Messages/UnitsEnum.java']
    basepath = os.path.dirname(os.path.realpath(__file__))
    srcpath = os.path.join(basepath, 'java/lib')
    print("Copying fixed headers")
    for h in hlist:
        src = os.path.realpath(os.path.join(srcpath, h))
        dest = os.path.realpath(os.path.join(directory, h))
        if src == dest:
            continue
        destdir = os.path.realpath(os.path.join(directory, 'Messages'))
        try:
            os.makedirs(destdir)
        except:
            print("Not re-creating Messages directory")
        shutil.copy(src, dest)

class mav_include(object):
    def __init__(self, base):
        self.base = base


def mavfmt(field, typeInfo=0):
    '''work out the struct format for a type'''
    map = {
        'float'    : ('float', 'Float'),
        'double'   : ('double', 'Double'),
        'char'     : ('byte', 'Byte'),
        'int8_t'   : ('byte', 'Byte'),
        'uint8_t'  : ('short', 'UnsignedByte'),
        'uint8_t_mavlink_version'  : ('short', 'UnsignedByte'),
        'int16_t'  : ('short', 'Short'),
        'uint16_t' : ('int', 'UnsignedShort'),
        'int32_t'  : ('int', 'Int'),
        'uint32_t' : ('long', 'UnsignedInt'),
        'int64_t'  : ('long', 'Long'),
        'uint64_t' : ('long', 'UnsignedLong'),
    }

    return map[field.type][typeInfo]

def generate_one(basename, xml):
    '''generate headers for one XML file'''

    directory = os.path.join(basename, xml.basename)

    print("Generating Java implementation in directory %s" % directory)
    mavparse.mkdir_p(directory)

    if xml.little_endian:
        xml.mavlink_endian = "MAVLINK_LITTLE_ENDIAN"
    else:
        xml.mavlink_endian = "MAVLINK_BIG_ENDIAN"

    if xml.crc_extra:
        xml.crc_extra_define = "1"
    else:
        xml.crc_extra_define = "0"

    if xml.sort_fields:
        xml.aligned_fields_define = "1"
    else:
        xml.aligned_fields_define = "0"

    # work out the included headers
    xml.include_list = []
    for i in xml.include:
        base = i[:-4]
        xml.include_list.append(mav_include(base))

    # form message lengths array
    xml.message_lengths_array = ''
    for mlen in xml.message_lengths:
        xml.message_lengths_array += '%u, ' % mlen
    xml.message_lengths_array = xml.message_lengths_array[:-2]



    # form message info array
    xml.message_info_array = ''
    for name in xml.message_names:
        if name is not None:
            xml.message_info_array += 'MAVLINK_MESSAGE_INFO_%s, ' % name
        else:
            # Several C compilers don't accept {NULL} for
            # multi-dimensional arrays and structs
            # feed the compiler a "filled" empty message
            xml.message_info_array += '{"EMPTY",0,{{"","",MAVLINK_TYPE_CHAR,0,0,0}}}, '
    xml.message_info_array = xml.message_info_array[:-2]

    # add some extra field attributes for convenience with arrays
    for m in xml.message:
        m.msg_name = m.name
        if xml.crc_extra:
            m.crc_extra_arg = ", %s" % m.crc_extra
        else:
            m.crc_extra_arg = ""
        for f in m.fields:
            if f.print_format is None:
                f.c_print_format = 'NULL'
            else:
                f.c_print_format = '"%s"' % f.print_format
            f.getText = ''
            if f.array_length != 0:
                f.array_suffix = '[] = new %s[%u]' % (mavfmt(f),f.array_length)
                f.array_suffix_empty = '[]'
                f.array_prefix = '*'
                f.array_tag = '_array'
                f.array_arg = ', %u' % f.array_length
                f.array_return_arg = '%s, %u, ' % (f.name, f.array_length)
                f.array_const = 'const '
                f.decode_left = ''
                f.decode_right = 'm.%s' % (f.name)

                f.unpackField = '''
        for (int i = 0; i < this.%s.length; i++) {
            this.%s[i] = payload.get%s();
        }
                ''' % (f.name, f.name, mavfmt(f, 1) )
                f.packField = '''
        for (int i = 0; i < %s.length; i++) {
            packet.payload.put%s(%s[i]);
        }
                    ''' % (f.name, mavfmt(f, 1),f.name)
                f.return_type = 'uint16_t'
                f.get_arg = ', %s *%s' % (f.type, f.name)
                if f.type == 'char':

                    f.c_test_value = '"%s"' % f.test_value
                    f.getText = '''
    /**
    * Sets the buffer of this message with a string, adds the necessary padding
    */
    public void set%s(String str) {
        int len = Math.min(str.length(), %d);
        for (int i=0; i<len; i++) {
            %s[i] = (byte) str.charAt(i);
        }

        for (int i=len; i<%d; i++) {            // padding for the rest of the buffer
            %s[i] = 0;
        }
    }

    /**
    * Gets the message, formatted as a string
    */
    public String get%s() {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < %d; i++) {
            if (%s[i] != 0)
                buf.append((char) %s[i]);
            else
                break;
        }
        return buf.toString();

    }
                        ''' % (f.name.title(),f.array_length,f.name,f.array_length,f.name,f.name.title(),f.array_length,f.name,f.name)
                else:
                    test_strings = []
                    for v in f.test_value:
                        test_strings.append(str(v))
                    f.c_test_value = '{ %s }' % ', '.join(test_strings)
            else:
                f.array_suffix = ''
                f.array_suffix_empty = ''
                f.array_prefix = ''
                f.array_tag = ''
                f.array_arg = ''
                f.array_return_arg = ''
                f.array_const = ''
                f.decode_left =  '%s' % (f.name)
                f.decode_right = ''
                f.unpackField = 'this.%s = payload.get%s();' % (f.name, mavfmt(f, 1))
                f.packField = 'packet.payload.put%s(%s);' % (mavfmt(f, 1),f.name)

                f.get_arg = ''
                f.return_type = f.type
                if f.type == 'char':
                    f.c_test_value = "'%s'" % f.test_value
                elif f.type == 'uint64_t':
                    f.c_test_value = "%sULL" % f.test_value
                elif f.type == 'int64_t':
                    f.c_test_value = "%sLL" % f.test_value
                else:
                    f.c_test_value = f.test_value

    # cope with uint8_t_mavlink_version
    for m in xml.message:
        m.arg_fields = []
        m.array_fields = []
        m.scalar_fields = []
        for f in m.ordered_fields:
            if f.array_length != 0:
                m.array_fields.append(f)
            else:
                m.scalar_fields.append(f)
        for f in m.fields:
            if not f.omit_arg:
                m.arg_fields.append(f)
                f.putname = f.name
            else:
                f.putname = f.const_value

    for m in xml.message:
        for f in m.ordered_fields:
            # fix types to java
            f.type = mavfmt(f)
            # remove brackets in units
            f.units = removeBrackets(f.units)
            # Escape quotes in description
            f.description = cleanText(f.description);

    # separate base fields from MAVLink 2 extended fields
    for m in xml.message:
        m.base_fields = m.ordered_fields[:m.extensions_start]
        m.extended_fields = []
        if m.extensions_start is not None:
            m.extended_fields = m.ordered_fields[m.extensions_start:]

    generate_CRC(directory, xml)

    for m in xml.message:
        generate_message_h(directory, m)

def removeBrackets(text):
    return text.replace("[","").replace("]","")

def cleanText(text):
    text = text.replace("\n"," ")
    text = text.replace("\r"," ")
    return text.replace("\"","'")

def generate(basename, xml_list):
    '''generate complete MAVLink Java implemenation'''
    for xml in xml_list:
        generate_one(basename, xml)
        generate_enums(basename, xml)
        generate_MAVLinkMessage(basename, xml_list)
    copy_fixed_headers(basename, xml_list[0])
